package 设计模式.单例模式;

import java.lang.reflect.Constructor;

public class Singleton {
}

/**
 * @ClassName HungrySingleton
 * @Description 饿汉式单例
 */
class HungrySingleton {
    //首次加载静态成员
    public static final HungrySingleton singleton = new HungrySingleton();

    //构造私有
    private HungrySingleton() {
    }

    //全局访问点
    public static HungrySingleton getInstance() {
        return singleton;
    }
}


/**
 * @ClassName HungryStaticSingleton
 * @Description 饿汉式单例---静态块初始化
 */
class HungryStaticSingleton {

    //final保证反射不会改变实例
    private static final HungryStaticSingleton instance;

    private HungryStaticSingleton() {
    }

    static {
        instance = new HungryStaticSingleton();
    }

    public static HungryStaticSingleton getInstance() {
        return instance;
    }
}


//优缺点总结
//优点：
//　　执行效率高，性能高【无锁】
//缺点：
//　　可能生成内存浪费，因为实例不被使用，也进行new .
//说明：
//　　这种写法，只能在小范围内使用。


/**
 * @ClassName LazySimpleSingleton
 * @Description 懒汉式单例
 * 优点：
 * 　　节省内存空间，使用时再创建实例。
 * 缺点：
 * 　　线程不安全。性能差。
 * 　　为什么会线程不安全呢？
 * 　　线程不安全来自于getInstance方法，方法的调用者最终归结到线程。
 * 当出现多个线程，同时调用getInstance方法时，线程间会抢占资源
 */
class LazySimpleSingleton {

    private static LazySimpleSingleton instance;

    private LazySimpleSingleton() {
    }

    public static LazySimpleSingleton getInstance() {
        if (null == instance) {
            instance = new LazySimpleSingleton();
        }
        return instance;
    }
}


/**
 * @ClassName LazySimpleSingleton
 * @Description 懒汉式单例
 */
class LazySimpleSingletonSync {

    private static LazySimpleSingletonSync instance;

    private LazySimpleSingletonSync() {
    }

    //synchronized关键字，是用来控制资源只能被一个线程占用，实现互斥特性。虽然，解决了线程安全性问题，但是代码性能受到限制。性能差。
    public synchronized static LazySimpleSingletonSync getInstance() {
        if (null == instance) {
            instance = new LazySimpleSingletonSync();
        }
        return instance;
    }
}


/**
 * @ClassName LazyDoubleCheckSingleton
 * @Description 双重检查--懒汉式单例
 * @Author wf
 * @Date 2020/4/30 12:41
 * @Version 1.0
 */
class LazyDoubleCheckSingleton {

    //volatile关键字解决指令重排序问题

    /**
     * 当在getInstance方法内部使用双重检查机制，解决了懒汉式单例的性能问题。
     * 但它存在指令重排序问题。
     * 创建一个对象包含下面两个过程：
     * 1、类构造器完成类初始化（分配内存、赋予默认值）
     * 2、类实例化（赋予给定值）
     * 在java中，当new一个实例时，会存在两块内存地址的处理：
     * 　　》创建后的实例会指向堆内存
     * 　　》成员赋值会指向栈内存
     * 两块内存的操作，先后顺序具有随机性，因此，可能会产生指令重排序问题。不过，没关系，java提供了volatile关键字，解决指令重排序问题。
     */
    private volatile static LazyDoubleCheckSingleton instance;

    private LazyDoubleCheckSingleton() {

    }

    public static LazyDoubleCheckSingleton getInstance() {
        //第一次检查，是否阻塞，只阻塞第一次new
        if (null == instance) {
            synchronized (LazyDoubleCheckSingleton.class) {
                //第二次检查，检查是否重复创建实例
                if (null == instance) {
                    instance = new LazyDoubleCheckSingleton();
                    //会有指令重排序问题
                }
            }
        }
        return instance;
    }

}
//双重检查机制，解决了线程安全性问题，也提升程序性能。但是，两个if判断，很容易让人迷惑，可读性差，代码不够优雅。

/**
 * @ClassName LazyStaticInnerClassSingleton
 * @Description 懒汉式-静态内部类实现单例
 */
class LazyStaticInnerClassSingleton {
    private LazyStaticInnerClassSingleton() {
    }

    private static LazyStaticInnerClassSingleton getInstance() {
        return LazyHolder.instance;
    }

    //静态内部类
    private static class LazyHolder {
        private static LazyStaticInnerClassSingleton instance = new LazyStaticInnerClassSingleton();

    }
}

//为什么这种方式是懒汉式呢，感觉很类似饿汉式单例？
//　　类加载机制，默认加载classpath下的*.class文件，而内部类在classpath下编译为XXX$LazyHolder.class【主类$内部类.class】
//　　所以，首次加载类时，不会加载内部类。只有当程序中使用到内部类时，才会加载内部类。
//总结：
//        　优点：写法优雅，很好利用java本身的语法特点，性能高，避免内存浪费。
//        　缺点：无法避免反射破坏。


//反射破坏单例测试
//        　　为什么反射会破坏单例呢？
//
//        　　因为代码中单例的实现时，基于构造器私有。如果通过反射能拿到类的实例，不就被破坏了吗。


/**
 * @ClassName ReflectTest
 * @Description 反射破坏单例
 */
class ReflectTest {
    public static void main(String[] args) {
        Class<?> clazz = LazyStaticInnerClassSingleton.class;
        try {
            Constructor<?> c = clazz.getDeclaredConstructor(null);
            System.out.println(c);
            c.setAccessible(true);//强制
            Object obj = c.newInstance();//通过反射，成功绕过单例类的全局访问点，创建实例
            Object obj1 = c.newInstance();
            System.out.println(obj);
            System.out.println(obj1);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}


//如何防止反射破坏单例
//        　　解决方法，是在构造器中做非空判断，如果非空，抛异常，提示不允许非法访问。


/**
 * @ClassName LazyStaticInnerClassSingleton
 * @Description 懒汉式-静态内部类实现单例
 */
class LazyStaticInnerClassSingletonReface {
    private LazyStaticInnerClassSingletonReface() {
        //防止反射破坏单例
        if (null != LazyHolder.instance) {
            throw new RuntimeException("不允许非法访问");
        }
    }

    private static LazyStaticInnerClassSingletonReface getInstance() {
        return LazyHolder.instance;
    }

    //静态内部类
    private static class LazyHolder {
        private static LazyStaticInnerClassSingletonReface instance = new LazyStaticInnerClassSingletonReface();

    }
}


//枚举式单例

/**
 * @ClassName EnumSingleton
 * @Description 枚举式单例
 */
enum EnumSingleton {
    INSTANCE;
    //枚举中可以自定义属性
    private Object data;

    public static EnumSingleton getInstance() {
        return INSTANCE;
    }

    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }
}

//ThreadLocal单例

/**
 * @ClassName ThreadLocalSingleton
 */
class ThreadLocalSingleton {
    private static final ThreadLocal<ThreadLocalSingleton> theadLocalInstance =
            new ThreadLocal<ThreadLocalSingleton>() {
                @Override
                protected ThreadLocalSingleton initialValue() {
                    return new ThreadLocalSingleton();
                }
            };

    private ThreadLocalSingleton() {
    }

    public static ThreadLocalSingleton getInstance() {
        return theadLocalInstance.get();
    }

}


//ThreadLocal线程局部，创建一个与线程绑定的变量，所以，它能隔离线程。它天生能保证线程安全。
//但是,ThreadLocal有其特殊性，它是线程绑定的。即不同线程获得实例，可能是不同的。
class ThreadLocalSingletonTest {
    public static void main(String[] args) {
        System.out.println(ThreadLocalSingleton.getInstance());
        System.out.println(ThreadLocalSingleton.getInstance());
        System.out.println(ThreadLocalSingleton.getInstance());

        Thread t1 = new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + ":" + ThreadLocalSingleton.getInstance());

        });
        Thread t2 = new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + ":" + ThreadLocalSingleton.getInstance());
        });
        t1.start();
        t2.start();
        System.out.println("end");
    }
}

//说明：
//　　似乎是产生了三个不同的实例。似乎是破坏了单例。但这是正常的。
//　　ThreadLocal只能保证同一线程下，只有一个实例。
//　　所以，这种单例是一种局限性单例。有其特殊的应用场景。
//
//因为：
//   线程的原因，是内部存在一个map维护数据，key是当前对象的所在的线程。

















































